﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Nonocast.Scheduling {
	public abstract class TaskRuleBase<T> : TaskRule<T> {
		public abstract string Type { get; }
		public T Context { get; set; }
		public int Priority { get; set; }
		public bool IsAudited { get; set; }

		public bool Enable { get; set; }
		public DateTime StartTime { get; set; }
		public DateTime? ExpirationTime { get; set; }

		public TaskRuleBase() {
			Enable = true;
		}

		public TaskRuleBase(T context)
			: this() {
			this.Context = context;
		}

		public bool HitTest(DateTime point) {
			throw new NotImplementedException();
		}

		public TaskRule<T> Cast<T>() {
			return this as TaskRule<T>;
		}

		public abstract Task<T> Generate(Task<T> reference = null);
	}

	public class Once<T> : TaskRuleBase<T>, Once {
		public override string Type { get { return "Once"; } }
		public DateTime SpecificDate { get; set; }

		public Once() : base() { }
		public Once(T context) : base(context) { }

		public override string ToString() {
			return string.Format("Once: {0}-{1} ({2})",
				StartTime.TimeOfDay, ExpirationTime.HasValue ? ExpirationTime.Value.TimeOfDay.ToString() : "n/a", SpecificDate.Date);
		}

		public override Task<T> Generate(Task<T> reference = null) {
			Task<T> result = null;
			if (reference == null) {
				DateTime now = DateTime.Now;
				DateTime once = new DateTime(SpecificDate.Year, SpecificDate.Month, SpecificDate.Day, StartTime.Hour, StartTime.Minute, StartTime.Second);
				if (now <= once) {
					result = new TaskImpl<T>(this) { StartTime = once };
				}
			} else {
				// just once, not any more
				result = null;
			}
			return result;
		}
	}

	public class DaysOfWeek<T> : TaskRuleBase<T>, DaysOfWeek {
		public override string Type { get { return "Week"; } }
		public WeekMask WeekMask { get; set; }

		public DaysOfWeek() : base() { }
		public DaysOfWeek(T context) : base(context) { }

		public override string ToString() {
			return string.Format("DaysOfWeek: {0}-{1} ({2})",
				StartTime.TimeOfDay, ExpirationTime.HasValue ? ExpirationTime.Value.TimeOfDay.ToString() : "n/a", WeekMask);
		}

		public override Task<T> Generate(Task<T> reference = null) {
			Task<T> result = null;
			DateTime startingPoint = reference == null ? DateTime.Now : reference.StartTime;
			foreach (var each in new DayRange().From(startingPoint.Date).UntilWholeWeek()) {
				DateTime p = new DateTime(each.Year, each.Month, each.Day, StartTime.Hour, StartTime.Minute, StartTime.Second);
				if (p > startingPoint && WeekMask.Contains(p.DayOfWeek)) {
					result = new TaskImpl<T>(this) { StartTime = p };
					break;
				}
			}

			// never will null
			Trace.Assert(result != null);
			return result;
		}
	}
}
